package feign;

import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

import feign.Request.Options;

@Experimental
public interface AsyncClient<C> {

    CompletableFuture<Response> execute(Request request, Options options, Optional<C> requestContext);

    class Default<C> implements AsyncClient<C> {

        private final Client client;
        private final ExecutorService executorService;

        public Default(Client client, ExecutorService executorService) {
            this.client = client;
            this.executorService = executorService;
        }

        @Override
        public CompletableFuture<Response> execute(Request request,
                                                   Options options,
                                                   Optional<C> requestContext) {
            final CompletableFuture<Response> result = new CompletableFuture<>();
            final Future<?> future = executorService.submit(() -> {
                try {
                    result.complete(client.execute(request, options));
                } catch (final Exception e) {
                    result.completeExceptionally(e);
                }
            });
            result.whenComplete((response, throwable) -> {
                if (result.isCancelled()) {
                    future.cancel(true);
                }
            });
            return result;
        }
    }

    class Pseudo<C> implements AsyncClient<C> {

        private final Client client;

        public Pseudo(Client client) {
            this.client = client;
        }

        @Override
        public CompletableFuture<Response> execute(Request request, Options options, Optional<C> requestContext) {
            final CompletableFuture<Response> result = new CompletableFuture<>();
            try {
                result.complete(client.execute(request, options));
            } catch (final Exception e) {
                result.completeExceptionally(e);
            }
            return result;
        }
    }
}
